// 版权所有2011 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package syscall

import (
	"internal/itoa"
	"internal/syscall/windows/sysdll"
	"sync"
	"sync/atomic"
	"unsafe"
)

// DLLError描述了DLL加载失败的原因。
type DLLError struct {
	Err     error
	ObjName string
	Msg     string
}

func (e *DLLError) Error() string { return e.Msg }

func (e *DLLError) Unwrap() error { return e.Err }

// 在../runtime/syscall_windows.go中实现。

func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno)
func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno)
func Syscall18(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2 uintptr, err Errno)
func loadlibrary(filename *uint16) (handle uintptr, err Errno)
func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno)
func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)

// 一个DLL实现对单个DLL的访问。
type DLL struct {
	Name   string
	Handle Handle
}

// 我们使用它来计算系统上系统DLL的绝对路径
// 其中搜索系统32不可用。
var systemDirectoryPrefix string

func init() {
	n := uint32(MAX_PATH)
	for {
		b := make([]uint16, n)
		l, e := getSystemDirectory(&b[0], n)
		if e != nil {
			panic("Unable to determine system directory: " + e.Error())
		}
		if l <= n {
			systemDirectoryPrefix = UTF16ToString(b[:l]) + "\\"
			break
		}
		n = l
	}
}

// LoadDLL将命名的DLL文件加载到内存中。
// 
// 如果名称不是绝对路径，并且不是
// Go使用的已知系统DLL，Windows将在许多位置搜索命名的DLL，导致
// 潜在的DLL预加载攻击。
// 
// 使用golang.org/x/sys/windows中的LazyDLL可以安全地加载系统DLL。
func LoadDLL(name string) (*DLL, error) {
	namep, err := UTF16PtrFromString(name)
	if err != nil {
		return nil, err
	}
	var h uintptr
	var e Errno
	if sysdll.IsSystemDLL[name] {
		absoluteFilepathp, err := UTF16PtrFromString(systemDirectoryPrefix + name)
		if err != nil {
			return nil, err
		}
		h, e = loadsystemlibrary(namep, absoluteFilepathp)
	} else {
		h, e = loadlibrary(namep)
	}
	if e != 0 {
		return nil, &DLLError{
			Err:     e,
			ObjName: name,
			Msg:     "Failed to load " + name + ": " + e.Error(),
		}
	}
	d := &DLL{
		Name:   name,
		Handle: Handle(h),
	}
	return d, nil
}

// MustLoadDLL与LoadDLL类似，但如果加载操作失败，则会出现恐慌。
func MustLoadDLL(name string) *DLL {
	d, e := LoadDLL(name)
	if e != nil {
		panic(e)
	}
	return d
}

// FindProc在DLL d中搜索名为name的过程，如果找到，则返回*Proc 
// 。如果搜索失败，它将返回一个错误。
func (d *DLL) FindProc(name string) (proc *Proc, err error) {
	namep, err := BytePtrFromString(name)
	if err != nil {
		return nil, err
	}
	a, e := getprocaddress(uintptr(d.Handle), namep)
	if e != 0 {
		return nil, &DLLError{
			Err:     e,
			ObjName: name,
			Msg:     "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
		}
	}
	p := &Proc{
		Dll:  d,
		Name: name,
		addr: a,
	}
	return p, nil
}

// MustFindProc与FindProc类似，但如果搜索失败，它会惊慌失措。
func (d *DLL) MustFindProc(name string) *Proc {
	p, e := d.FindProc(name)
	if e != nil {
		panic(e)
	}
	return p
}

// 释放将DLL d从内存中卸载。
func (d *DLL) Release() (err error) {
	return FreeLibrary(d.Handle)
}

// Proc实现对DLL内过程的访问。
type Proc struct {
	Dll  *DLL
	Name string
	addr uintptr
}

// Addr返回由p表示的过程的地址。
// 返回值可以传递给Syscall以运行该过程。
func (p *Proc) Addr() uintptr {
	return p.addr
}

// go:uintTrescapes 

// 调用使用参数a执行过程p。如果提供了超过18个参数
// 它将陷入恐慌。
// 
// 返回的错误始终为非零，由GetLastError的结果构造而成。
// 调用者必须先检查主返回值以确定是否发生错误
// （根据被调用的特定函数的语义），然后再咨询
// 错误。该错误的类型始终为syscall.Errno。
// 
// 在amd64上，调用可以传递和返回浮点值。要传递带有C类型“float”的参数x，请使用
// uintptpr（math.Float32bits（x））。要传递C类型为
// /“double”的参数，请使用uintpttr（math.float64位（x））。浮点返回
// 在r2中返回值。C类型“float”的返回值是
// math.Float32frombits（uint32（r2））。对于C类型的“double”，它是
// /math.Float64frombits（uint64（r2））。
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
	switch len(a) {
	case 0:
		return Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
	case 1:
		return Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
	case 2:
		return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
	case 3:
		return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
	case 4:
		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
	case 5:
		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
	case 6:
		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
	case 7:
		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
	case 8:
		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
	case 9:
		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])
	case 10:
		return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0)
	case 11:
		return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0)
	case 12:
		return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11])
	case 13:
		return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0)
	case 14:
		return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0)
	case 15:
		return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14])
	case 16:
		return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], 0, 0)
	case 17:
		return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], 0)
	case 18:
		return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], a[17])
	default:
		panic("Call " + p.Name + " with too many arguments " + itoa.Itoa(len(a)) + ".")
	}
}

// LazyDLL实现对单个DLL的访问。
// 它将延迟DLL的加载，直到第一个
// 调用它的Handle方法或它的一个
// LazyProc的Addr方法。
// 
// LazyDLL受到与LoadDLL上记录的
// 相同的DLL预加载攻击。
// 
// 使用golang.org/x/sys/windows中的LazyDLL可以安全地加载系统DLL。
type LazyDLL struct {
	mu   sync.Mutex
	dll  *DLL // 加载DLL后非零
	Name string
}

// 加载将DLL文件d.Name加载到内存中。如果失败，它将返回一个错误。
// 如果DLL已经加载到内存中，Load将不会尝试加载DLL。
func (d *LazyDLL) Load() error {
	// 的非racy版本：
	// 如果d.dll==nil{
	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) == nil {
		d.mu.Lock()
		defer d.mu.Unlock()
		if d.dll == nil {
			dll, e := LoadDLL(d.Name)
			if e != nil {
				return e
			}
			// 非racy版本：
			// d.dll=dll 
			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
		}
	}
	return nil
}

// mustLoad与Load类似，但在搜索失败时会惊慌失措。
func (d *LazyDLL) mustLoad() {
	e := d.Load()
	if e != nil {
		panic(e)
	}
}

// Handle返回d的模块句柄。
func (d *LazyDLL) Handle() uintptr {
	d.mustLoad()
	return uintptr(d.dll.Handle)
}

// NewProc返回用于访问dll d中指定过程的LazyProc。
func (d *LazyDLL) NewProc(name string) *LazyProc {
	return &LazyProc{l: d, Name: name}
}

// NewLazyDLL创建新的LazyDLL与DLL文件关联。
func NewLazyDLL(name string) *LazyDLL {
	return &LazyDLL{Name: name}
}

// LazyProc实现对LazyDLL内过程的访问。
// 它延迟查找，直到调用Addr、Call或Find方法。
type LazyProc struct {
	mu   sync.Mutex
	Name string
	l    *LazyDLL
	proc *Proc
}

// Find在DLL中搜索名为p.Name的过程。如果搜索失败，它将返回
// 错误。Find将不搜索proceduree、 
// 如果已经找到并加载到内存中。
func (p *LazyProc) Find() error {
	// 的非racy版本：
	// 如果p.proc==nil{
	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
		p.mu.Lock()
		defer p.mu.Unlock()
		if p.proc == nil {
			e := p.l.Load()
			if e != nil {
				return e
			}
			proc, e := p.l.dll.FindProc(p.Name)
			if e != nil {
				return e
			}
			// 
			// p.proc=proc 
			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
		}
	}
	return nil
}

// mustFind与Find类似，但在搜索失败时会惊慌失措。
func (p *LazyProc) mustFind() {
	e := p.Find()
	if e != nil {
		panic(e)
	}
}

// Addr返回由p.
// 返回值可以传递给Syscall以运行该过程。
func (p *LazyProc) Addr() uintptr {
	p.mustFind()
	return p.proc.Addr()
}

// go:uintprescapes 

// Call使用参数a执行过程p。有关详细信息，请参阅
// Proc.Call的文档。
func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
	p.mustFind()
	return p.proc.Call(a...)
}
